#include "dialog.h"
#include "ui_dialog.h"

#include <QMetaProperty>

void Dialog::do_ageChanged(int value)
{
    Q_UNUSED(value)

    TPerson *person = qobject_cast<TPerson *>(sender());
    /**
     * 不能通过 person->name()的方式获取 name 的值
     * 不能通过 person->sex()的方式获取 sex 的值
     * 不能通过 person->score()的方式获取 score 的值
     * 因为没有其属性没有提供获取值的函数
     * 所以必须使用 person->property 的方式获取对应字段的值
     * 获取到的值是一个 QVariant 类型变量，需要转换为实际的类型
     * QVariant 类型的放入和取出必须是相对应的
     */
    QString hisName = person->property("name").toString();
    QString hisSex = person->property("sex").toString();
    int hisScore = person->property("score").toInt();
    /**
     * 可以使用如下形式获取 person 的 age
     * int hisAge = person->age();
     * 等价于
     * int hisAge = person->property("age").toInt();
     */
    int hisAge = person->age();

    QString str = QString::fromUtf8("姓名: %1 | 年龄: %2 | 性别: %3 | 分数: %4 ")
                      .arg(hisName).arg(hisAge).arg(hisSex).arg(hisScore);
    ui->txtEdit->appendPlainText(str);
}

void Dialog::do_spinChanged(int age)
{
    QSpinBox *spinBox = qobject_cast<QSpinBox *>(sender());
    if (spinBox->property("isBoy").toBool()) {
        boy->setAge(age);
    } else {
        girl->setAge(age);
    }
}

void Dialog::on_btnBoyInc_clicked()
{
    ui->spinBoxBoy->setValue(boy->property("age").toInt() + 1);
}

void Dialog::on_btnGirlInc_clicked()
{
    ui->spinBoxGirl->setValue(girl->property("age").toInt() + 1);
}

void Dialog::on_btnMetaInfo_clicked()
{
    appendTextMessage(boy);
    appendTextMessage(girl);
}

void Dialog::appendTextMessage(QObject *obj)
{
    const QMetaObject *meta = obj->metaObject();

    ui->txtEdit->appendPlainText(QString::fromUtf8("=== 元对象信息  ===\n"));
    ui->txtEdit->appendPlainText(QString::fromUtf8("类名称: ") + QString::fromUtf8(meta->className()));
    ui->txtEdit->appendPlainText(QChar::fromLatin1('\n'));

    ui->txtEdit->appendPlainText(QString::fromUtf8("property:"));
    for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) {
        const char *propName = meta->property(i).name();
        ui->txtEdit->appendPlainText(QString::fromUtf8("%1 = %2").arg(QString::fromUtf8(propName)).arg(obj->property(propName).toString()));
    }
    ui->txtEdit->appendPlainText(QChar::fromLatin1('\n'));

    ui->txtEdit->appendPlainText(QString::fromUtf8("classInfo:"));
    for (int i = meta->classInfoOffset(); i < meta->classInfoCount(); ++i) {
        QMetaClassInfo classInfo = meta->classInfo(i);
        ui->txtEdit->appendPlainText(QString::fromUtf8("%1 = %2").arg(QString::fromUtf8(classInfo.name())).arg(QString::fromUtf8(classInfo.value())));
    }
    ui->txtEdit->appendPlainText(QChar::fromLatin1('\n'));

    ui->txtEdit->appendPlainText(QString::fromUtf8("==========\n"));
}

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    /**
     * 初始化 boy 信息
     */
    boy = new TPerson(QStringLiteral("王小明"), this);
    boy->setProperty("score", 95);
    boy->setProperty("age", 19);
    boy->setProperty("sex", "Boy"); /* 动态属性 */
    /**
     * 对于具有默认参数的信号与槽 ( 即信号名称是唯一的，没有参数不同而同名的两个信号 )
     * 可以使用这种函数指针形式进行关联
     * connect(boy, SIGNAL(ageChanged(int)), this, SLOT(do_ageChanged(int)))
     * 等价于如下标准形式
     * connect(boy, &TPerson::ageChanged, this, &Dialog::do_ageChanged)
     */
    connect(boy, SIGNAL(ageChanged(int)), this, SLOT(do_ageChanged(int)));

    girl = new TPerson(QStringLiteral("张小丽"), this);
    girl->setProperty("score", 81);
    girl->setProperty("age", 17);
    girl->setProperty("sex", "girl"); /* 动态属性 */
    /**
     * 对于具有默认参数的信号与槽 ( 即信号名称是唯一的，没有参数不同而同名的两个信号 )
     * 可以使用这种函数指针形式进行关联
     * connect(boy, SIGNAL(ageChanged(int)), this, SLOT(do_ageChanged(int)))
     * 等价于如下标准形式
     * connect(boy, &TPerson::ageChanged, this, &Dialog::do_ageChanged)
     */
    connect(girl, SIGNAL(ageChanged(int)), this, SLOT(do_ageChanged(int)));

    ui->spinBoxBoy->setProperty("isBoy", true); /* 动态属性 */
    ui->spinBoxGirl->setProperty("isBoy", false); /* 动态属性 */
    ui->spinBoxBoy->setValue(boy->property("age").toInt()); /* 设置默认值 */
    ui->spinBoxGirl->setValue(girl->property("age").toInt()); /* 设置默认值 */
    /**
     * 不能使用函数指针形式的进行信号与槽的连接
     * 因为 QSpinBox 有两种参数形式的 valueChanged()信号
     * 所以只能使用标准形式的信号与槽连接形式
     */
    connect(ui->spinBoxBoy, SIGNAL(valueChanged(int)), this, SLOT(do_spinChanged(int)));
    connect(ui->spinBoxGirl, SIGNAL(valueChanged(int)), this, SLOT(do_spinChanged(int)));
}

Dialog::~Dialog()
{
    if (ui) {
        delete ui;
        ui = nullptr;
    }
}
